/**
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
 *
 */

import { hash, hvigorCore } from '@ohos/hvigor';
import { ArkCompileProjectModelType } from '@ohos/hvigor-arkts-compose';
import fse from 'fs-extra';
import path from 'path';

import { findTargetProduct } from '../../common/find-target-product.js';
import { CommonConst } from '../../const/common-const.js';
import { ProjectModel } from '../../model/project/project-model.js';
import { ProjectBuildProfile } from '../../options/build/project-build-profile.js';
import { FileUtil } from '../../utils/file-util.js';
import { RemoteHspCacheOpt, SignedRemoteHspOpt } from '../../utils/remote-hsp-utils.js';
import SigningConfigBuildOpt = ProjectBuildProfile.SigningConfigBuildOpt;
import { isEqual } from '@baize/wdk';

import { IntegratedHspUtils } from '../../utils/integrated-hsp-utils.js';
import { getJson5Obj } from '../../utils/json-util.js';
import { OhosLogger } from '../../utils/log/ohos-logger.js';
import { getOhosTestIntermediatesEtsPath, isTemplatesAfterSourceCodeMigration } from '../../utils/ohos-test-util.js';
import { getSigningConfig } from '../sign/sign-util.js';
import { HvigorConfigLoader } from '@ohos/hvigor/src/common/util/hvigor-config-loader';

export interface BuildModeOpt {
  ifThrowError: boolean;
  buildModeName: string;
  buildProfilePath: string;
}

/**
 * 全局的工程信息，只需要计算一次，模块级别任务可直接获取，不需要重复计算
 */
export class GlobalProjectDataService {
  private static INSTANCE: GlobalProjectDataService = new GlobalProjectDataService();
  private compileProjectModel: ArkCompileProjectModelType = {};
  private allModulePaths: string[] = [];
  private projectPkgJsonFileHash = '';
  private allModuleNameHash = '';
  private rootPathSet: Set<string> = new Set<string>();
  private isEmptyTargetSdkVersionCheck = false;
  private buildModeInfo: BuildModeOpt = {
    ifThrowError: false,
    buildModeName: '',
    buildProfilePath: '',
  };

  // 该字段用于限制project级的remoteHsp签名只签一次
  // undefined为未签名状态
  // 判断已经有签名任务后将projectRemoteHspPromise赋值为Promise对象
  private projectRemoteHspPromise: Promise<void> | undefined;

  // 该字段用于限制module级的remoteHsp签名只签一次
  private moduleRemoteHspSet: Set<string> = new Set<string>();

  // 该字段用于对所有需要签名的remoteHsp包进行统一收集和签名，避免重复签名
  private remoteHspCache: RemoteHspCacheOpt = {
    signingConfig: {} as SigningConfigBuildOpt,
    signedRemoteHsps: new Map<string, SignedRemoteHspOpt>(),
  };
  private integratedHspUtils: IntegratedHspUtils | undefined;

  private _log: OhosLogger = OhosLogger.getLogger(GlobalProjectDataService.name);

  public static getInstance(): GlobalProjectDataService {
    return GlobalProjectDataService.INSTANCE;
  }

  public initGlobalProjectData(projectModel: ProjectModel): void {
    this.rootPathSet = new Set<string>();
    // 初始化以防止daemon错误缓存上次的内容
    this.allModulePaths = [];
    this.compileProjectModel = this.initCompileProjectModel(projectModel);
    this.allModuleNameHash = this.initAllModuleNameHash(projectModel);
    this.projectRemoteHspPromise = undefined;
    this.moduleRemoteHspSet = new Set<string>();
    this.initRemoteHspCache(projectModel);
    this.integratedHspUtils = new IntegratedHspUtils(projectModel);
    this.buildModeInfo = {
      ifThrowError: false,
      buildModeName: '',
      buildProfilePath: '',
    };
    this.isEmptyTargetSdkVersionCheck = false;
  }

  public getIsEmptyTargetSdkVersionCheck() {
    return this.isEmptyTargetSdkVersionCheck;
  }

  public setIsEmptyTargetSdkVersionCheck() {
    this.isEmptyTargetSdkVersionCheck = true;
  }

  public getCompileProjectModel(): ArkCompileProjectModelType {
    return this.compileProjectModel;
  }

  public setProjectPkgJsonFileHash(projectPkgHash: string) {
    this.projectPkgJsonFileHash = projectPkgHash;
  }

  public getProjectPkgJsonFileHash(): string {
    return this.projectPkgJsonFileHash;
  }

  public getAllModuleNameHash(): string {
    return this.allModuleNameHash;
  }

  public setProjectRemoteHspPromise(promise: Promise<void>): void {
    this.projectRemoteHspPromise = promise;
  }

  public getProjectRemoteHspPromise() {
    return this.projectRemoteHspPromise;
  }

  public setModuleRemoteHspPromise(moduleName: string): void {
    this.moduleRemoteHspSet.add(moduleName);
  }

  public getModuleRemoteHspPromise(): Set<string> {
    return this.moduleRemoteHspSet;
  }

  public setNeedSignedRemoteHspMap(hspDirName: string, obj: SignedRemoteHspOpt): void {
    this.remoteHspCache.signedRemoteHsps.set(hspDirName, obj);
  }

  public getRemoteHspCache(): RemoteHspCacheOpt {
    return this.remoteHspCache;
  }

  public getBuildModeInfo(): BuildModeOpt {
    return this.buildModeInfo;
  }

  public setBuildModeInfo(newBuildModeInfo: BuildModeOpt): BuildModeOpt {
    return this.buildModeInfo = newBuildModeInfo;
  }

  /**
   * 判断remoteHsp是否需要签名，用于去重
   * @param hspDirName  hsp目录名
   * @param moduleName  模块名
   */
  public needSignedRemoteHsp(hspDirName: string, moduleName: string): boolean {
    // 签名文件存在缓存中需要判断是否是回填的hsp，如果是则重写信息并触发签名操作
    if (this.remoteHspCache.signedRemoteHsps.has(hspDirName)) {
      const remoteHsp = this.remoteHspCache.signedRemoteHsps.get(hspDirName);

      // 不存在moduleName则证明不是回填hsp，不需要重新签名
      if (!remoteHsp?.moduleName) {
        return false;
      }

      return remoteHsp!.moduleName === moduleName;
    }
    return true;
  }

  /**
   * 将map中的isSigned字段修改为true，用于判断是否需要写入缓存文件
   * @param signedHspPath 被签名的hsp路径
   */
  public changeRemoteHspSigned(signedHspPath: string): void {
    for (const item of this.remoteHspCache.signedRemoteHsps.values()) {
      if (item.signedHspPath === signedHspPath) {
        item.isSigned = true;
      }
    }
  }

  /**
   * 判断map中的isSigned是否都为true用于判断是否需要写入缓存文件
   */
  public allRemoteHspSigned(): boolean {
    for (const item of this.remoteHspCache.signedRemoteHsps.values()) {
      if (!item.isSigned) {
        return false;
      }
    }
    return true;
  }

  public getIntegratedHspUtils(): IntegratedHspUtils | undefined {
    return this.integratedHspUtils;
  }

  public getAllModulePaths(): string[] {
    return this.allModulePaths;
  }

  private initCompileProjectModel(projectModel: ProjectModel): ArkCompileProjectModelType {
    const compileProjectModel: ArkCompileProjectModelType = {};
    projectModel.getAllModules().forEach((module) => {
      this.rootPathSet.add(module.getBelongProjectPath());
      const modulePath = module.getProjectDir();
      this.allModulePaths.push(modulePath);
      for (const [targetName, sourceSet] of module.getAllTargetSourceSet()) {
        const moduleName = sourceSet.getTargetSourceSetModuleName() ?? module.getName();
        const moduleSourceRoot = sourceSet.getSourceSetRoot();
        if (targetName === 'ohosTest' && isTemplatesAfterSourceCodeMigration(moduleSourceRoot)) {
          const newSourceRoot = getOhosTestIntermediatesEtsPath(modulePath);
          compileProjectModel[newSourceRoot] = {
            moduleName,
            modulePkgPath: modulePath,
            belongProjectPath: module.getBelongProjectPath(),
          };
        }
        compileProjectModel[moduleSourceRoot] = {
          moduleName,
          modulePkgPath: modulePath,
          belongProjectPath: module.getBelongProjectPath(),
        };
      }
      compileProjectModel[modulePath] = {
        moduleName: module.getName(),
        modulePkgPath: modulePath,
        belongProjectPath: module.getBelongProjectPath(),
      };
      const buildCacheDir =
        hvigorCore.getExtraConfig().get(CommonConst.BUILD_CACHE_DIR) ??
        HvigorConfigLoader.getInstance().getPropertiesConfigValue<string>(CommonConst.BUILD_DIR_KEY);
      if (buildCacheDir) {
        fse.ensureDirSync(buildCacheDir);
        this.rootPathSet.add(path.resolve(buildCacheDir, projectModel.getName()));
        // 针对ModuleInfo文件,如果使用自定义构建产物目录,需重新定义产物路径
        const customModuleCachePath = path.resolve(buildCacheDir, projectModel.getName(), module.getName());
        compileProjectModel[customModuleCachePath] = {
          moduleName: module.getName(),
          modulePkgPath: module.getProjectDir(),
          belongProjectPath: module.getBelongProjectPath(),
          isBuildCacheDir: true,
        };
        this.allModulePaths.push(customModuleCachePath);
      }
    });
    compileProjectModel[projectModel.getProjectDir()] = {
      moduleName: projectModel.getName(),
      modulePkgPath: projectModel.getProjectDir(),
      belongProjectPath: projectModel.getProjectDir(),
    };
    return compileProjectModel;
  }

  public initProjectPkgJsonFileHash(projectModel: ProjectModel): string {
    const isOhpmProject = projectModel.isOhpmProject();
    let pkgJsonContext = projectModel
      .getAllModules()
      .map((module) => {
        const pkgJsonPath = isOhpmProject ? module.getOhPackageJson5Path() : module.getPackageJsonPath();
        return FileUtil.readFile(pkgJsonPath) ?? '';
      })
      .join('');
    const pkgJsonPath = isOhpmProject ? projectModel.getOhPackageJson5Path() : projectModel.getPackageJsonPath();
    pkgJsonContext += FileUtil.readFile(pkgJsonPath) ?? '';
    return hash(pkgJsonContext);
  }

  private initAllModuleNameHash(projectModel: ProjectModel) {
    let allModuleName = projectModel
      .getAllModules()
      .map((module) => {
        let moduleName = '';
        for (const [targetName, sourceSet] of module.getAllTargetSourceSet()) {
          moduleName += sourceSet.getTargetSourceSetModuleName() ?? module.getName();
        }
        return moduleName;
      })
      .join('');
    allModuleName += projectModel.getName();
    return hash(allModuleName);
  }

  /**
   * 获取各个模块的belongProjectPath的映射关系
   */
  public getRootPathSet(): Set<string> {
    return this.rootPathSet;
  }

  public resetRemoteHspCache() {
    this.remoteHspCache = {
      signingConfig: {} as SigningConfigBuildOpt,
      signedRemoteHsps: new Map<string, SignedRemoteHspOpt>(),
    };
  }

  /**
   * 读取缓存文件进行初始化，如果存在签名包则不重复签名
   * @param projectModel
   * @private
   */
  private initRemoteHspCache(projectModel: ProjectModel) {
    this.resetRemoteHspCache();
    const currentProduct: ProjectBuildProfile.ProductBuildOpt = findTargetProduct(projectModel);
    const signingConfig = getSigningConfig(projectModel);
    const projectHspDir = projectModel.getCacheRemoteHspPath(currentProduct.name);
    const projectRemoteHspCachePath = path.resolve(projectHspDir, CommonConst.REMOTE_HSP_CACHE);
    const map = new Map<string, SignedRemoteHspOpt>();
    if (!signingConfig) {
      this._log.debug('No signingConfig found, initRemoteHspCache failed.');
      return;
    }

    // 如果存在remoteHspCache.json文件，则判断签名信息是否一致，如果不一致全部重新签名
    if (fse.existsSync(projectRemoteHspCachePath)) {
      const packageJson: any = getJson5Obj(projectRemoteHspCachePath);

      // 如果存在该文件并且签名信息相同，则根据缓存和目录将不需要重复签名的包记录下来
      if (isEqual(packageJson.signingConfig, signingConfig)) {
        const signedRemoteHspNames = fse.readdirSync(projectHspDir);
        Object.keys(packageJson.signedRemoteHsps).forEach((key) => {
          const cache = packageJson.signedRemoteHsps[key];
          if (signedRemoteHspNames.includes(key) && cache?.signedHspPath && fse.existsSync(cache.signedHspPath)) {
            map.set(key, cache);
          }
        });
        this.remoteHspCache.signedRemoteHsps = map;
      }
    }
    this.remoteHspCache.signingConfig = signingConfig;
  }
}
